/**********************************************************************************
// Game Source
//
// Creation:	24 Ago 2007
// Updated:		30 Set 2009
// Compiler:	Visual Studio 2008
//
// Notes:		The Game class has all the pixel plotting functions that access  
//              the video card memory directly, and all the blitting functions 
//              that draw using the graphics hardware.
//
**********************************************************************************/

#include "Game.h"

/**********************************************************************************/

Game::Game()
{
	clientWindow.left   = 0;
	clientWindow.top    = 0;
	clientWindow.right  = GetWindowWidth() - 1;
	clientWindow.bottom = GetWindowHeight() - 1;
}

/**********************************************************************************/

Game::~Game(){}

/**********************************************************************************/

void Game::SetWindowWidth(int width)
{
	WindowsGame::SetWindowWidth(width);
	clientWindow.right = width - 1;
}
	

/**********************************************************************************/

void Game::SetWindowHeight(int height)
{
	WindowsGame::SetWindowHeight(height);
	clientWindow.bottom = height - 1;
}

/**********************************************************************************/

void Game::Draw(const Point& point, const Color& col)
{
	int x = point.IntX();
	int y = point.IntY();

	if (x > clientWindow.left && 
		x < clientWindow.right &&
		y > clientWindow.top &&
		y < clientWindow.bottom)
		DrawFastPixel(x, y, col.Encoded());    
}

/**********************************************************************************/

void Game::Draw(const Line& line, const Color& col)
{
	int x1 = line.X1();
	int y1 = line.Y1();
	int x2 = line.X2();
	int y2 = line.Y2();

	// if line is not entirely out of screen
	if (ClipLine(x1, y1, x2, y2))
		DrawFastLine(x1, y1, x2, y2, col.Encoded());
}

/**********************************************************************************/

void Game::Draw(const Rect& rect, const Color& col)
{

	Line top    = Line(rect.GetLeft(), rect.GetTop(), rect.GetRight(), rect.GetTop());
	Line left   = Line(rect.GetLeft(), rect.GetTop()+1, rect.GetLeft(), rect.GetBottom());
	Line right  = Line(rect.GetRight(), rect.GetTop()+1, rect.GetRight(), rect.GetBottom());
	Line bottom = Line(rect.GetLeft()+1, rect.GetBottom(), rect.GetRight()-1, rect.GetBottom());

	Draw(top, col);
	Draw(left, col);
	Draw(right, col);
	Draw(bottom, col);
}

/**********************************************************************************/

void Game::Draw(const Circle& circle, const Color& col)
{
	// Bresenham's circle algorithm

	float radius = circle.GetRadius();
	float xpos   = circle.x;
	float ypos   = circle.y;

	float d = 3 - (2 * radius);
	float x = 0;
	float y = radius;

	Draw(Point(xpos, ypos+radius), col);
	Draw(Point(xpos, ypos-radius), col);
	Draw(Point(xpos, ypos+radius), col);
	Draw(Point(xpos, ypos-radius), col);
	Draw(Point(xpos+radius, ypos), col);
	Draw(Point(xpos+radius, ypos), col);
	Draw(Point(xpos-radius, ypos), col);
	Draw(Point(xpos-radius, ypos), col);

	for(x=1; x <= int(radius/sqrt(2.0f)); x++)
	{  
		if (d < 0)
			d = d + (4 * x) + 6;
		else
		{
			d = d + 4 * (x - y) + 10;
			y = y - 1;
		}

		Draw(Point(xpos+x, ypos+y), col);
		Draw(Point(xpos+x, ypos-y), col);
		Draw(Point(xpos-x, ypos+y), col);
		Draw(Point(xpos-x, ypos-y), col);
		Draw(Point(xpos+y, ypos+x), col);
		Draw(Point(xpos+y, ypos-x), col);
		Draw(Point(xpos-y, ypos+x), col);
		Draw(Point(xpos-y, ypos-x), col);
	}
}

/**********************************************************************************/

void Game::Draw(const Poly& poly, const Color& col)
{
	// this function draws a Poly

	float x1, y1, x2, y2;
	int i;

	// get vertice list
	Point * vList = poly.GetVertices();

	// loop through and draw a line from vertices 1 to n-1
	for (i=0; i < poly.GetNumVerts()-1; ++i)
    {
		// draw line from ith to ith+1 vertex
		x1 = vList[i].x + poly.x;
		y1 = vList[i].y + poly.y;
		x2 = vList[i+1].x + poly.x;
		y2 = vList[i+1].y + poly.y;
		
		// draw a line (clipping to window rectangle)
		Draw(Line(x1, y1, x2, y2), col);
    }

	// now close up polygon
	// draw line from first to last vertex
	x1 = vList[0].x + poly.x;
	y1 = vList[0].y + poly.y;
	x2 = vList[i].x + poly.x;
	y2 = vList[i].y + poly.y;
			
	// draw a line (clipping to window rectangle)
	Draw(Line(x1, y1, x2, y2), col);
}

/**********************************************************************************/

void Game::Draw(const Bitmap &bmp, int xpos, int ypos)
{
	// get screen coordinates of the image
	int x1 = xpos;
	int y1 = ypos;
	int x2 = xpos + bmp.GetImageWidth() - 1;
	int y2 = ypos + bmp.GetImageHeight() - 1;

	// validate screen coordinates with respect to the clipping rectangle
	if (x1 < clientWindow.left)
		x1 = clientWindow.left;
	if (x2 > clientWindow.right)
		x2 = clientWindow.right;
	if (y1 < clientWindow.top)
		y1 = clientWindow.top;
	if (y2 > clientWindow.bottom)
		y2 = clientWindow.bottom;

	// now convert the screen coordinates back to image coordinates
	x1 = x1 - xpos;
	y1 = y1 - ypos;
	x2 = x2 - xpos;
	y2 = y2 - ypos;

	DrawFastImage(bmp, x1, y1, x2, y2, xpos, ypos);
}


/**********************************************************************************/

void Game::DrawFastLine(int a1, int b1, int a2, int b2, DWORD color)
{
	// Symmetric Double Step Line Algorithm by Xialon Wu
	// It's 3 to 4 times faster than the standard Bressenham's algorithm
	// Implementation by Brian Wyvill from "Graphics Gems", Academic Press, 1990
	
	int dx, dy, incr1, incr2, D, x, y, xend, c, pixels_left;
	int x1, y1;
	int sign_x, sign_y, step, reverse, i;

	dx = (a2-a1) * (sign_x = ((a2-a1)<0 ? -1 : 1));
	dy = (b2-b1) * (sign_y = ((b2-b1)<0 ? -1 : 1));

	/* decide increment sign by the slope sign */
	if (sign_x == sign_y)
		step = 1;
	else
		step = -1;

	/* chooses axis of greatest movement (make dx) */
	if (dy > dx) 
	{	
		// operator ^= obtain the bitwise exclusive OR of the first and second operands; 
		// store the result in the object specified by the first operand
		a1^=b1; b1^=a1; a1^=b1;
		a2^=b2; b2^=a2; a2^=b2;
		dx^=dy; dy^=dx; dx^=dy;
		reverse = 1;
	} 
	else
		reverse = 0;

	/* note error check for dx==0 should be included here */
	/* start from the smaller coordinate */
	if (a1 > a2) 
	{		
		x = a2;
		y = b2;
		x1 = a1;
		y1 = b1;
	} 
	else 
	{
		x = a1;
		y = b1;
		x1 = a2;
		y1 = b2;
	}

	/* Note dx=n implies 0 - n or (dx+1) pixels to be set 
	 * Go round loop dx/4 times then plot last 0,1,2 or 3 pixels 
	 *  In fact (dx-1)/4 as 2 pixels are already plotted */
	xend = (dx - 1) / 4;
	pixels_left = (dx - 1) % 4;	/* number of pixels left over at the end */
	Plot(x, y, reverse, color);
	if ( pixels_left < 0 ) 
		return ;	/* plot only one pixel for zero
					 * length vectors */
	Plot(x1, y1, reverse, color);	/* plot first two points */
	incr2 = 4 * dy - 2 * dx;
	if (incr2 < 0) {	/* slope less than 1/2 */
		c = 2 * dy;
		incr1 = 2 * c;
		D = incr1 - dx;

		for (i = 0; i < xend; i++) {	/* plotting loop */
			++x;
			--x1;
			if (D < 0) {
                  			/* pattern 1 forwards */
				Plot(x, y, reverse, color);
				Plot(++x, y, reverse, color);
                                /* pattern 1 backwards */
				Plot(x1, y1, reverse, color);
				Plot(--x1, y1, reverse, color);
				D += incr1;
			} else {
				if (D < c) {
					/* pattern 2 forwards */
					Plot(x, y, reverse, color);
					Plot(++x, y += step, reverse, color);
					/* pattern 2 backwards */
					Plot(x1, y1, reverse, color);
					Plot(--x1, y1 -= step, reverse, color);	
				} else {
				        /* pattern 3 forwards */
					Plot(x, y += step, reverse, color);
					Plot(++x, y, reverse, color);
					/* pattern 3 backwards */
					Plot(x1, y1 -= step, reverse, color);
					Plot(--x1, y1, reverse, color);
				}
				D += incr2;
			}
		}		/* end for */
		/* plot last pattern */
		if (pixels_left) {
			if (D < 0) {
				Plot(++x, y, reverse, color);	/* pattern 1 */
				if (pixels_left > 1)
					Plot(++x, y, reverse, color);
				if (pixels_left > 2)
					Plot(--x1, y1, reverse, color);
			} else {
				if (D < c) {
					Plot(++x, y, reverse, color);	/* pattern 2  */
					if (pixels_left > 1)
						Plot(++x, y += step, reverse, color);
					if (pixels_left > 2)
						Plot(--x1, y1, reverse, color);
				} else {
				  /* pattern 3 */
					Plot(++x, y += step, reverse, color);
					if (pixels_left > 1)
						Plot(++x, y, reverse, color);
					if (pixels_left > 2)
						Plot(--x1, y1 -= step, reverse, color);
				}
			}
		}		/* end if pixels_left */
	}
	/* end slope < 1/2 */
	else {			/* slope greater than 1/2 */
		c = 2 * (dy - dx);
		incr1 = 2 * c;
		D = incr1 + dx;
		for (i = 0; i < xend; i++) {
			++x;
			--x1;
			if (D > 0) {
			  /* pattern 4 forwards */
				Plot(x, y += step, reverse, color);
				Plot(++x, y += step, reverse, color);
			  /* pattern 4 backwards */
				Plot(x1, y1 -= step, reverse, color);
				Plot(--x1, y1 -= step, reverse, color);
				D += incr1;
			} else {
				if (D < c) {
				  /* pattern 2 forwards */
					Plot(x, y, reverse, color);
					Plot(++x, y += step, reverse, color);

 				  /* pattern 2 backwards */
					Plot(x1, y1, reverse, color);
					Plot(--x1, y1 -= step, reverse, color);
				} else {
				  /* pattern 3 forwards */
					Plot(x, y += step, reverse, color);
					Plot(++x, y, reverse, color);
				  /* pattern 3 backwards */
					Plot(x1, y1 -= step, reverse, color);
					Plot(--x1, y1, reverse, color);
				}
				D += incr2;
			}
		}		/* end for */
		/* plot last pattern */
		if (pixels_left) {
			if (D > 0) {
				Plot(++x, y += step, reverse, color);	/* pattern 4 */
				if (pixels_left > 1)
					Plot(++x, y += step, reverse, color);
				if (pixels_left > 2)
					Plot(--x1, y1 -= step, reverse, color);
			} else {
				if (D < c) {
					Plot(++x, y, reverse, color);	/* pattern 2  */
					if (pixels_left > 1)
						Plot(++x, y += step, reverse, color);
					if (pixels_left > 2)
						Plot(--x1, y1, reverse, color);
				} else {
				  /* pattern 3 */
					Plot(++x, y += step, reverse, color);
					if (pixels_left > 1)
						Plot(++x, y, reverse, color);
					if (pixels_left > 2) {
						if (D > c) /* step 3 */
						   Plot(--x1, y1 -= step, reverse, color);
						else /* step 2 */
							Plot(--x1, y1, reverse, color);
                         		}
				}
			}
		}
	}
}

/**********************************************************************************/

int Game::ClipLine(int &x1, int &y1, int &x2, int &y2)
{

	// Clipping Line Algorithm 
	// Implementation by Andre Lamonte from "Tricks of The Windows Game Programming Gurus", SAMS, 2002

	// this function clips the sent line using the clipping region defined above
	int min_clip_x = clientWindow.left;
	int min_clip_y = clientWindow.top;
	int max_clip_x = clientWindow.right;
	int max_clip_y = clientWindow.bottom;

	// internal clipping codes
	#define CLIP_CODE_C  0x0000
	#define CLIP_CODE_N  0x0008
	#define CLIP_CODE_S  0x0004
	#define CLIP_CODE_E  0x0002
	#define CLIP_CODE_W  0x0001
	
	#define CLIP_CODE_NE 0x000a
	#define CLIP_CODE_SE 0x0006
	#define CLIP_CODE_NW 0x0009 
	#define CLIP_CODE_SW 0x0005

	int xc1=x1, 
		yc1=y1, 
		xc2=x2, 
		yc2=y2;

	int p1_code=0, 
	    p2_code=0;

	// determine codes for p1 and p2
	if (y1 < min_clip_y)
		p1_code|=CLIP_CODE_N;
	else
	if (y1 > max_clip_y)
		p1_code|=CLIP_CODE_S;

	if (x1 < min_clip_x)
		p1_code|=CLIP_CODE_W;
	else
	if (x1 > max_clip_x)
		p1_code|=CLIP_CODE_E;

	if (y2 < min_clip_y)
		p2_code|=CLIP_CODE_N;
	else
	if (y2 > max_clip_y)
		p2_code|=CLIP_CODE_S;

	if (x2 < min_clip_x)
		p2_code|=CLIP_CODE_W;
	else
	if (x2 > max_clip_x)
		p2_code|=CLIP_CODE_E;

	// try and trivially reject
	if ((p1_code & p2_code)) 
		return 0;

	// test for totally visible, if so leave points untouched
	if (p1_code==0 && p2_code==0)
		return 1;

	// determine end clip point for p1
	switch(p1_code)
	{
		case CLIP_CODE_C: break;

		case CLIP_CODE_N:
		{
		    yc1 = min_clip_y;
		    xc1 = int(x1 + 0.5+(min_clip_y-y1)*(x2-x1)/(y2-y1));
		} break;
		case CLIP_CODE_S:
		{
		    yc1 = max_clip_y;
		    xc1 = int(x1 + 0.5+(max_clip_y-y1)*(x2-x1)/(y2-y1));
		} break;

		case CLIP_CODE_W:
		{
		    xc1 = min_clip_x;
		    yc1 = int(y1 + 0.5+(min_clip_x-x1)*(y2-y1)/(x2-x1));
		} break;
		
		case CLIP_CODE_E:
		{
			xc1 = max_clip_x;
			yc1 = int(y1 + 0.5+(max_clip_x-x1)*(y2-y1)/(x2-x1));
		} break;

		// these cases are more complex, must compute 2 intersections
		case CLIP_CODE_NE:
		{
		    // north hline intersection
		    yc1 = min_clip_y;
		    xc1 = int(x1 + 0.5+(min_clip_y-y1)*(x2-x1)/(y2-y1));

		    // test if intersection is valid, of so then done, else compute next
			if (xc1 < min_clip_x || xc1 > max_clip_x)
			{
				// east vline intersection
				xc1 = max_clip_x;
				yc1 = int(y1 + 0.5+(max_clip_x-x1)*(y2-y1)/(x2-x1));
			} // end if

		} break;
	  
		case CLIP_CODE_SE:
      	{
			// south hline intersection
		    yc1 = max_clip_y;
		    xc1 = int(x1 + 0.5+(max_clip_y-y1)*(x2-x1)/(y2-y1));	

		    // test if intersection is valid, of so then done, else compute next
		    if (xc1 < min_clip_x || xc1 > max_clip_x)
		    {
			  // east vline intersection
			  xc1 = max_clip_x;
			  yc1 = int(y1 + 0.5+(max_clip_x-x1)*(y2-y1)/(x2-x1));
			} // end if

		} break;
	    
		case CLIP_CODE_NW: 
      	{
			// north hline intersection
		    yc1 = min_clip_y;
		    xc1 = int(x1 + 0.5+(min_clip_y-y1)*(x2-x1)/(y2-y1));
		   
		    // test if intersection is valid, of so then done, else compute next
		    if (xc1 < min_clip_x || xc1 > max_clip_x)
		    {
				xc1 = min_clip_x;
		        yc1 = int(y1 + 0.5+(min_clip_x-x1)*(y2-y1)/(x2-x1));	
			} // end if

		} break;
	  	  
		case CLIP_CODE_SW:
		{
			// south hline intersection
		    yc1 = max_clip_y;
		    xc1 = int(x1 + 0.5+(max_clip_y-y1)*(x2-x1)/(y2-y1));	
		   
		    // test if intersection is valid, of so then done, else compute next
		    if (xc1 < min_clip_x || xc1 > max_clip_x)
		    {
				xc1 = min_clip_x;
				yc1 = int(y1 + 0.5+(min_clip_x-x1)*(y2-y1)/(x2-x1));	
			} // end if

		} break;

		default:break;
	} // end switch

	// determine clip point for p2
	switch(p2_code)
	{
	  case CLIP_CODE_C: break;

	  case CLIP_CODE_N:
		   {
		   yc2 = min_clip_y;
		   xc2 = x2 + (min_clip_y-y2)*(x1-x2)/(y1-y2);
		   } break;

	  case CLIP_CODE_S:
		   {
		   yc2 = max_clip_y;
		   xc2 = x2 + (max_clip_y-y2)*(x1-x2)/(y1-y2);
		   } break;

	  case CLIP_CODE_W:
		   {
		   xc2 = min_clip_x;
		   yc2 = y2 + (min_clip_x-x2)*(y1-y2)/(x1-x2);
		   } break;
		
	  case CLIP_CODE_E:
		   {
		   xc2 = max_clip_x;
		   yc2 = y2 + (max_clip_x-x2)*(y1-y2)/(x1-x2);
		   } break;

		// these cases are more complex, must compute 2 intersections
	  case CLIP_CODE_NE:
		   {
		   // north hline intersection
		   yc2 = min_clip_y;
		   xc2 = int(x2 + 0.5+(min_clip_y-y2)*(x1-x2)/(y1-y2));

		   // test if intersection is valid, of so then done, else compute next
			if (xc2 < min_clip_x || xc2 > max_clip_x)
				{
				// east vline intersection
				xc2 = max_clip_x;
				yc2 = int(y2 + 0.5+(max_clip_x-x2)*(y1-y2)/(x1-x2));
				} // end if

		   } break;
	  
	  case CLIP_CODE_SE:
      	   {
		   // south hline intersection
		   yc2 = max_clip_y;
		   xc2 = int(x2 + 0.5+(max_clip_y-y2)*(x1-x2)/(y1-y2));	

		   // test if intersection is valid, of so then done, else compute next
		   if (xc2 < min_clip_x || xc2 > max_clip_x)
		      {
			  // east vline intersection
			  xc2 = max_clip_x;
			  yc2 = int(y2 + 0.5+(max_clip_x-x2)*(y1-y2)/(x1-x2));
			  } // end if

		   } break;
	    
	  case CLIP_CODE_NW: 
      	   {
		   // north hline intersection
		   yc2 = min_clip_y;
		   xc2 = int(x2 + 0.5+(min_clip_y-y2)*(x1-x2)/(y1-y2));
		   
		   // test if intersection is valid, of so then done, else compute next
		   if (xc2 < min_clip_x || xc2 > max_clip_x)
		      {
			  xc2 = min_clip_x;
		      yc2 = int(y2 + 0.5+(min_clip_x-x2)*(y1-y2)/(x1-x2));	
			  } // end if

		   } break;
	  	  
	  case CLIP_CODE_SW:
		   {
		   // south hline intersection
		   yc2 = max_clip_y;
		   xc2 = int(x2 + 0.5+(max_clip_y-y2)*(x1-x2)/(y1-y2));	
		   
		   // test if intersection is valid, of so then done, else compute next
		   if (xc2 < min_clip_x || xc2 > max_clip_x)
		      {
			  xc2 = min_clip_x;
		      yc2 = int(y2 + 0.5+(min_clip_x-x2)*(y1-y2)/(x1-x2));	
			  } // end if

		   } break;
	
	  default:break;

	} // end switch

	// do bounds check
	if ((xc1 < min_clip_x) || (xc1 > max_clip_x) ||
		(yc1 < min_clip_y) || (yc1 > max_clip_y) ||
		(xc2 < min_clip_x) || (xc2 > max_clip_x) ||
		(yc2 < min_clip_y) || (yc2 > max_clip_y) )
	{
		return 0;
	}

	// store vars back
	x1 = xc1;
	y1 = yc1;
	x2 = xc2;
	y2 = yc2;

	return 1;
} 

/**********************************************************************************/

void Game::DrawFastImage(const Bitmap &bmp, int imgX1, int imgY1, int imgX2, int imgY2, int xpos, int ypos)
{
	// DrawFastImage shows the part of the bitmap data defined by 
	// the rectangle (imgX1, imgY1) to (imgX2, imgY2)

	// the function assumes the cordinates (imgX1, imgY1) to (imgX2, imgY2)
	// are within the image boundaries

	int width       = bmp.GetImageWidth();
	int height      = bmp.GetImageHeight();
	UCHAR * imgData = bmp.GetImageData();

	// process each line and copy it into the primary buffer
	for (int y = imgY1; y <= imgY2; y++)
		for (int x = imgX1; x <= imgX2; x++)
		{
			// get BGR values
			UCHAR blue  = (imgData[y*width*3 + x*3 + 0]),
				  green = (imgData[y*width*3 + x*3 + 1]),
				  red   = (imgData[y*width*3 + x*3 + 2]);

			// write the pixel
			DrawFastPixel(xpos+x, ypos+y, Color(red, green, blue).Encoded());
		}
}

/**********************************************************************************/
